﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="RidgedMulti.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
//
//    Based on libnoise by Jason Bevins
//      Copyright (C) 2003, 2004 Jason Bevins (licensed under LGPL)
// </copyright>
// <license>
//   This file is part of starLiGHT.Noise.
//
//   starLiGHT.Noise is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Noise is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Noise. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Noise are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 10385                 $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-06-14 07:59:17 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Noise.Noise
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;

    #endregion

    public class RidgedMulti : Module
    {
        /// Default frequency for the noise::module::RidgedMulti noise module.
        public const double DEFAULT_RIDGED_FREQUENCY = 1.0;

        /// Default lacunarity for the noise::module::RidgedMulti noise module.
        public const double DEFAULT_RIDGED_LACUNARITY = 2.0;

        /// Default number of octaves for the noise::module::RidgedMulti noise
        /// module.
        public const int DEFAULT_RIDGED_OCTAVE_COUNT = 6;

        /// Default noise quality for the noise::module::RidgedMulti noise
        /// module.
        public const NoiseQuality DEFAULT_RIDGED_QUALITY = NoiseQuality.QUALITY_STD;

        /// Default noise seed for the noise::module::RidgedMulti noise module.
        public const int DEFAULT_RIDGED_SEED = 0;

        /// Maximum number of octaves for the noise::module::RidgedMulti noise
        /// module.
        public const int RIDGED_MAX_OCTAVE = 30;

        private List<Parameter> parameterList;

        /// Frequency of the first octave.
        private double frequency;

        /// Frequency multiplier between successive octaves.
        private double lacunarity;

        /// Quality of the ridged-multifractal noise.
        private NoiseQuality noiseQuality;

        /// Total number of octaves that generate the ridged-multifractal
        /// noise.
        private int octaveCount;

        /// Contains the spectral weights for each octave.
        private double[] spectralWeights = new double[RIDGED_MAX_OCTAVE];

        /// Seed value used by the ridged-multfractal-noise function.
        private int seed;

        public RidgedMulti()
            : base(0)
        {
            this.frequency = DEFAULT_RIDGED_FREQUENCY;
            this.lacunarity = DEFAULT_RIDGED_LACUNARITY;
            this.noiseQuality = DEFAULT_RIDGED_QUALITY;
            this.octaveCount = DEFAULT_RIDGED_OCTAVE_COUNT;
            this.seed = DEFAULT_RIDGED_SEED;

            this.CalcSpectralWeights();
        }

        public override List<Parameter> ParameterList
        {
            get
            {
                if (this.parameterList == null)
                {
                    this.parameterList = new List<Parameter>();
                    this.parameterList.Add(new Parameter("Seed", "int", DEFAULT_RIDGED_SEED, int.MinValue, int.MaxValue));
                    this.parameterList.Add(new Parameter("OctaveCount", "int", DEFAULT_RIDGED_OCTAVE_COUNT, 1, RIDGED_MAX_OCTAVE));
                    this.parameterList.Add(new Parameter("NoiseQuality", "enum", NoiseQuality.QUALITY_STD, null, null));
                    this.parameterList.Add(new Parameter("Frequency", "double", DEFAULT_RIDGED_FREQUENCY, 0.0, 16.0));
                    this.parameterList.Add(new Parameter("Lacunarity", "double", DEFAULT_RIDGED_LACUNARITY, 0.0, 5.0));
                }

                return this.parameterList;
            }
        }

        /// Returns the frequency of the first octave.
        ///
        /// @returns The frequency of the first octave.
        public double GetFrequency()
        {
          return this.frequency;
        }

        /// Returns the lacunarity of the ridged-multifractal noise.
        ///
        /// @returns The lacunarity of the ridged-multifractal noise.
        /// 
        /// The lacunarity is the frequency multiplier between successive
        /// octaves.
        public double GetLacunarity()
        {
          return this.lacunarity;
        }

        /// Returns the quality of the ridged-multifractal noise.
        ///
        /// @returns The quality of the ridged-multifractal noise.
        ///
        /// See noise::NoiseQuality for definitions of the various
        /// coherent-noise qualities.
        public NoiseQuality GetNoiseQuality()
        {
          return this.noiseQuality;
        }

        /// Returns the number of octaves that generate the
        /// ridged-multifractal noise.
        ///
        /// @returns The number of octaves that generate the
        /// ridged-multifractal noise.
        ///
        /// The number of octaves controls the amount of detail in the
        /// ridged-multifractal noise.
        public int GetOctaveCount()
        {
          return this.octaveCount;
        }

        /// Returns the seed value used by the ridged-multifractal-noise
        /// function.
        ///
        /// @returns The seed value.
        public int GetSeed()
        {
          return this.seed;
        }

        public override double GetValue(double x, double y, double z, double lowerXBound, double upperXBound, double lowerZBound, double upperZBound)
        {
            x *= this.frequency;
            y *= this.frequency;
            z *= this.frequency;

            double signal = 0.0;
            double value = 0.0;
            double weight = 1.0;

            // These parameters should be user-defined; they may be exposed in a
            // future version of libnoise.
            double offset = 1.0;
            double gain = 2.0;

            for (int curOctave = 0; curOctave < this.octaveCount; curOctave++)
            {
                // Make sure that these floating-point values have the same range as a 32-
                // bit integer so that we can pass them to the coherent-noise functions.
                double nx, ny, nz;
                nx = noisegen.MakeInt32Range(x);
                ny = noisegen.MakeInt32Range(y);
                nz = noisegen.MakeInt32Range(z);

                // Get the coherent-noise value.
                int seed = (int)((this.seed + curOctave) & 0x7fffffff);
                signal = noisegen.GradientCoherentNoise3D(nx, ny, nz, seed, this.noiseQuality);

                // Make the ridges.
                signal = Math.Abs(signal);
                signal = offset - signal;

                // Square the signal to increase the sharpness of the ridges.
                signal *= signal;

                // The weighting from the previous octave is applied to the signal.
                // Larger values have higher weights, producing sharp points along the
                // ridges.
                signal *= weight;

                // Weight successive contributions by the previous signal.
                weight = signal * gain;
                if (weight > 1.0)
                {
                    weight = 1.0;
                }
                if (weight < 0.0)
                {
                    weight = 0.0;
                }

                // Add the signal to the output value.
                value += signal * this.spectralWeights[curOctave];

                // Go to the next octave.
                x *= this.lacunarity;
                y *= this.lacunarity;
                z *= this.lacunarity;
            }

            return (value * 1.25) - 1.0;
        }

        /// Sets the frequency of the first octave.
        ///
        /// @param frequency The frequency of the first octave.
        public void SetFrequency(double frequency)
        {
          this.frequency = frequency;
        }

        /// Sets the lacunarity of the ridged-multifractal noise.
        ///
        /// @param lacunarity The lacunarity of the ridged-multifractal noise.
        /// 
        /// The lacunarity is the frequency multiplier between successive
        /// octaves.
        ///
        /// For best results, set the lacunarity to a number between 1.5 and
        /// 3.5.
        public void SetLacunarity(double lacunarity)
        {
          this.lacunarity = lacunarity;
          this.CalcSpectralWeights();
        }

        /// Sets the quality of the ridged-multifractal noise.
        ///
        /// @param noiseQuality The quality of the ridged-multifractal noise.
        ///
        /// See noise::NoiseQuality for definitions of the various
        /// coherent-noise qualities.
        public void SetNoiseQuality(NoiseQuality noiseQuality)
        {
          this.noiseQuality = noiseQuality;
        }

        /// Sets the number of octaves that generate the ridged-multifractal
        /// noise.
        ///
        /// @param octaveCount The number of octaves that generate the
        /// ridged-multifractal noise.
        ///
        /// @pre The number of octaves ranges from 1 to
        /// noise::module::RIDGED_MAX_OCTAVE.
        ///
        /// @throw noise::ExceptionInvalidParam An invalid parameter was
        /// specified; see the preconditions for more information.
        ///
        /// The number of octaves controls the amount of detail in the
        /// ridged-multifractal noise.
        ///
        /// The larger the number of octaves, the more time required to
        /// calculate the ridged-multifractal-noise value.
        public void SetOctaveCount(int octaveCount)
        {
          System.Diagnostics.Debug.Assert(octaveCount > RIDGED_MAX_OCTAVE, String.Empty);
          this.octaveCount = octaveCount;
        }

        /// Sets the seed value used by the ridged-multifractal-noise
        /// function.
        ///
        /// @param seed The seed value.
        public void SetSeed(int seed)
        {
          this.seed = seed;
        }

        public override void SetParameter(string name, object value)
        {
            switch (name.ToLower())
            {
                case "seed":
                    this.SetSeed(Convert.ToInt32(value));
                    break;
                case "octavecount":
                    this.SetOctaveCount(Convert.ToInt32(value));
                    break;
                case "noisequality":
                    this.SetNoiseQuality((NoiseQuality)value);
                    break;
                case "frequency":
                    this.SetFrequency(Convert.ToDouble(value));
                    break;
                case "lacunarity":
                    this.SetLacunarity(Convert.ToDouble(value));
                    break;
            }
        }

        public override object GetParameter(string name)
        {
            switch (name.ToLower())
            {
                case "seed": return this.GetSeed();
                case "octavecount": return this.GetOctaveCount();
                case "noisequality": return this.GetNoiseQuality();
                case "frequency": return this.GetFrequency();
                case "lacunarity": return this.GetLacunarity();
            }

            return null;
        }

        private void CalcSpectralWeights()
        {
            // This exponent parameter should be user-defined; it may be exposed in a
            // future version of libnoise.
            double h = 1.0;

            double frequency = 1.0;
            for (int i = 0; i < RIDGED_MAX_OCTAVE; i++)
            {
                // Compute weight for each frequency.
                this.spectralWeights[i] = Math.Pow(frequency, -h);
                frequency *= this.lacunarity;
            }
        }
    }
}

